#include<GL/glut.h>
#include<iostream>
#include<cmath>
typedef struct{
    float x,y,z;
}point3D;

//整型数组
typedef struct{
    int length;
    int * arr;
}intArray;

//坐标数组
typedef struct{
    int length;
    point3D * arr;
}pointArray;

//该方法接受经过计算的贝塞尔点，并对相邻两个点进行连线处理
void drawLine(pointArray &bezPts);

//此方法用来计算二项式系数
//接受一个数组对象的引用
void binomialCoefficient(intArray &C){
    int j,n=C.length-1;
    for(int k=0;k<=n;k++){
        C.arr[k] = 1;
        for(j=n;j>k;j--)
            C.arr[k] *= j;//分子累乘
        for(j=n-k;j>1;j--)
            C.arr[k] /= j;//分母累除
    }
}

//此方法计算在第规律u处点的坐标位置
//接受的第一个参数为规律u，第二个参数为将要存放位置点的指针，第三个参数为所有控制点坐标数组，第五个参数为系数数组)
void computeBezPt(float u,point3D &bezPt,pointArray &ctrlPts,intArray &C){
    int n=ctrlPts.length-1;
    float bezFcn;//保存贝塞尔方程计算结果
    bezPt.x=bezPt.y=bezPt.z=0;//初始化
    for(int k=0;k<=n;k++){
        bezFcn = C.arr[k]*pow(u,k)*pow(1-u,n-k);
        bezPt.x+=ctrlPts.arr[k].x*bezFcn;
        bezPt.y+=ctrlPts.arr[k].y*bezFcn;
        bezPt.z+=ctrlPts.arr[k].z*bezFcn;
    }
}

//此方法为调用方法，用来产生贝塞尔曲线
//第一个参数用来接受控制点数组，第二个参数为精度，精度越高两个点之间的距离越小，但是需要的计算时间也就越长
void bezier(pointArray &ctrlPts,int precision){
    intArray C;
    C.length=ctrlPts.length;
    C.arr = new int[C.length];
    binomialCoefficient(C);//计算系数

    pointArray bezPts;//保存计算点的参数
    bezPts.length=precision+1;
    bezPts.arr = new point3D[bezPts.length];

    float u;//规律参数
    for(int k=0;k<=precision;k++){
        u = float(k)/float(precision);
        computeBezPt(u,bezPts.arr[k],ctrlPts,C);
    }
    drawLine(bezPts);
    delete[] bezPts.arr;
    delete[] C.arr;
}
void drawLine(pointArray &bezPts){
    glBegin(GL_LINE_STRIP);
    for(int i=0;i<bezPts.length;i++){
        glVertex3f(bezPts.arr[i].x,bezPts.arr[i].y,bezPts.arr[i].z);
    }
    glEnd();
}
static pointArray ctrlPts_;
void displayFcn(void){
    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f(1,0,0);
    bezier(ctrlPts_,100);
    glFlush();
}
int main(int argc, char* argv[]){
    ctrlPts_.length = 4;
    point3D ctrlPts[4] = {{-40,-40,0},{-10,200,0},{10,-200,0},{40,40,0}};
    ctrlPts_.arr= ctrlPts;

    glutInit(&argc,argv);
    glutInitDisplayMode(GLUT_SINGLE|GLUT_RGBA);
    glutInitWindowPosition(50,50);
    glutInitWindowSize(600,600);
    glutCreateWindow("bezier curve");
    glClearColor(1,1,1,0);
    glMatrixMode(GL_PROJECTION);
    gluOrtho2D(-50,50,-50,50);
    glutDisplayFunc(displayFcn);
    glutMainLoop();
    return 0;
}
